package com.malty.gateway.sign;

import com.malty.common.ErrorCode;
import com.malty.common.exception.BusinessException;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.DigestUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * post 请求 application/x-www-form-urlencoded 方式 签名验证
 *
 * @author malty
 */
public class FormUrlEncodedApiSignStrategy extends ApiSignStrategy {
    @Override
    public String doCalculateSign(String signKey, String sourceStr) {
        String plaintext = String.format("%s_digest_%s", signKey, sourceStr);
        return DigestUtils.md5DigestAsHex(plaintext.getBytes(StandardCharsets.UTF_8));
    }

    @Override
    public Mono<Map<String, String>> getParamMap(ServerWebExchange exchange) {
        ServerHttpRequest request = exchange.getRequest();
        HttpHeaders headers = request.getHeaders();

        // 先判断Content-Type是不是application/json
        MediaType contentType = headers.getContentType();
        if (contentType == null || !MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) {
            return Mono.error(new BusinessException(ErrorCode.PARAMS_ERROR, "Content-Type must be application/json"));
        }

        // 获取缓存的 DataBuffer
        DataBuffer cachedBodyBuffer = exchange.getAttribute(ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR);
        // 如果没有缓存的请求体或者请求体为空
        if (cachedBodyBuffer == null || cachedBodyBuffer.readableByteCount() == 0) {
            Map<String, String> paramMap = new HashMap<>(extractHeaderParam(request));
            paramMap.remove("sign");
            return Mono.just(Collections.unmodifiableMap(paramMap));
        }

        String bodyStr = cachedBodyBuffer.toString(StandardCharsets.UTF_8);
        Map<String, String> paramMap = new HashMap<>();
        if (!bodyStr.isEmpty()) {
            Arrays.stream(bodyStr.split("&"))
                    .map(pair -> pair.split("=", 2))
                    .filter(arr -> arr.length == 2 && !arr[0].isEmpty())
                    .map(arr -> {
                        try {
                            String key = URLDecoder.decode(arr[0], StandardCharsets.UTF_8.name());
                            String value = URLDecoder.decode(arr[1], StandardCharsets.UTF_8.name());
                            return new AbstractMap.SimpleEntry<>(key, value);
                        } catch (Exception e) {
                            // log.warn("Failed to decode parameter: {}", String.join("=", arr), e);
                            return null;
                        }
                    }).filter(entry -> entry != null && entry.getKey() != null && !entry.getKey().isEmpty())
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v2));
        }

        paramMap.putAll(extractHeaderParam(request));
        paramMap.remove("sign");
        return Mono.just(Collections.unmodifiableMap(paramMap));
    }
}
